﻿/**

 * Copyright (c) 2015-2017, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */


using Mono.Cecil;
using Mono.Cecil.Cil;
using OF.ILModifier.Common;
using OF.ILModifier.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;

namespace OF.ILModifier.Extension
{
    public static class MethodDefinitionExtension
    {
        public static string VoidTypeFullName = string.Join(TypeReferenceExtension.Seq, new string[] { "mscorlib", "System.Void" });
        public static MethodDefinition CopyEmptyBody(this MethodDefinition thisObj, string name)
        {
            ConstructorInfo conInfo = typeof(MethodDefinition).GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, new Type[0], null);
            MethodDefinition result = conInfo.Invoke(null) as MethodDefinition; //new MethodDefinition();
            var declaringType = typeof(MethodDefinition);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "attributes" }, thisObj, result);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "impl_attributes" }, thisObj, result);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "sem_attrs_ready" }, thisObj, result);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "sem_attrs" }, thisObj, result);
            FieldHelper.Set(new FieldKey { DeclaringType = declaringType, FieldName = "custom_attributes" }, result, thisObj.CustomAttributes);
            FieldHelper.Set(new FieldKey { DeclaringType = declaringType, FieldName = "security_declarations" }, result, thisObj.SecurityDeclarations);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "pinvoke" }, thisObj, result);
            FieldHelper.Set(new FieldKey { DeclaringType = declaringType, FieldName = "overrides" }, result, thisObj.Overrides);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "debug_info" }, thisObj, result);
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "custom_infos" }, thisObj, result);
            result.ReturnType = thisObj.ReturnType;
            result.HasThis = thisObj.HasThis;
            result.ExplicitThis = thisObj.ExplicitThis;
            result.CallingConvention = thisObj.CallingConvention;
            result.Name = name;
            FieldHelper.Copy(new FieldKey { DeclaringType = declaringType, FieldName = "projection" }, thisObj, result);
            result.DeclaringType = thisObj.DeclaringType;
            thisObj.CloneParams(result);
            thisObj.CloneGenericParams(result);
            result.Body.InitLocals = thisObj.Body.InitLocals;
            return result;
        }

        public static void SetExceptionHandler(this MethodDefinition thisObj, ModuleDefinition module, MethodDefinition result)
        {
            foreach (var exceptionHandler in thisObj.Body.ExceptionHandlers)
            {
                result.Body.ExceptionHandlers.Add(exceptionHandler.Clone(module));
            }
        }

        public static void SetVariables(this MethodDefinition thisObj, ModuleDefinition module, MethodDefinition result)
        {
            result.Body.Variables.Clear();
            foreach (var variable in thisObj.Body.Variables)
            {
                result.Body.Variables.Add(variable.Clone(module, thisObj));
            }
        }

        public static void InitReturnVariable(this MethodDefinition thisObj)
        {
            thisObj.Body.Variables.Clear();
            if (thisObj.HasReturnValue())
            {
                thisObj.Body.Variables.Add(new VariableDefinition(thisObj.ReturnType));
            }
        }


        private static void CloneParams(this MethodDefinition thisObj, MethodDefinition reference)
        {
            foreach (var parameter in thisObj.Parameters)
            {
                reference.Parameters.Add(new ParameterDefinition(parameter.ParameterType));
            }
        }

        private static void CloneGenericParams(this MethodDefinition thisObj, MethodDefinition reference)
        {
            foreach (var generic_parameter in thisObj.GenericParameters)
            {
                reference.GenericParameters.Add(new GenericParameter(generic_parameter.Name, reference));
            }
        }
    }
}
